package com.csizg.facemodel.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import com.csizg.facemodel.R;
import com.csizg.facemodel.strategy.ICompareStrategy;
import com.csizg.facemodel.strategy.LbphUpdateStrategy;
import com.csizg.facemodel.tracker.DetectionBasedTracker;
import com.csizg.facemodel.util.CompareResult;
import com.csizg.facemodel.util.FaceFileUtil;
import com.csizg.facemodel.util.FaceUtil;
import com.csizg.facemodel.util.RawUtil;
import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.Logger;
import java.io.File;
import java.util.Locale;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CsizgCameraView;
import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

/**
 * 竖屏识别 训练识别方案
 *
 * @author haohao(ronghao3508@gmail.com) on 2018/5/7 17:51
 * @version v1.0
 */
public class MyNDFaceCameraView extends CsizgCameraView
        implements CameraBridgeViewBase.CvCameraViewListener2 {

    public static final int SHAPE_SQUARE = 0;
    public static final int SHAPE_QUADRATE = 1;

    public static final String TAG = "MyFaceCameraView";
    private static final String FACE1 = "currentface1";
    private static final String FACE2 = "currentface";

    //边框颜色
    public static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255);

    public float mRelativeFaceSize = 0.3f;//脸部识别大小
    public int mAbsoluteFaceSize = 0;
    public DetectionBasedTracker mNativeDetector;//ndk分类器（检测）

    private Mat mRgba;
    private Mat mGray;
    private Mat matForBorder;
    private Mat matForDetect;
    private Mat mCache90Mat;
    private Bitmap mCacheBitmap;
    private Size mNarrowSize;

    private ICompareListener iCompareListener;
    private IFaceShowListener iFaceShowListener;

    private int lengthPassNumber = 3;//连续几次成功还算识别成功
    private int captureFaceMax = 3;//默认截取几张图片
    private volatile int captureFaceCurrent;//当前截取人脸数量
    private int captureId = 1;//默认识别id
    private volatile boolean needCompare = false;//是否比较
    private volatile int tmpConfirmNumber = 0;//当前重复正确过的次数
    private boolean hasTrain = false;//是否训练过

    private ICompareStrategy compareStrategy = new LbphUpdateStrategy();//比较策略

    private boolean isShowResult = false;

    private double similarity = 0;

    public MyNDFaceCameraView(Context context, int cameraId) {
        super(context, cameraId);
        Logger.addLogAdapter(new AndroidLogAdapter());
        init();
    }

    public MyNDFaceCameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Logger.addLogAdapter(new AndroidLogAdapter());
        init();
    }

    public void init() {
        Log.e("启动时间", "开始启动");
        setCvCameraViewListener(this);
        disableView();
        setCameraIndex(CAMERA_ID_FRONT);
        setMinFaceSize(mRelativeFaceSize);
        enableView();
        Log.e("启动时间", "启动完成");
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
        Log.e("启动时间", "显示准备开始");
        Log.e("图片尺寸1", "mScale = " + mScale + "，width = " + width + "，height = " + height);
        if(width == 0 || height == 0){
            return;
        }
        mGray = new Mat();
        mRgba = new Mat();
        mCache90Mat = new Mat();

        matForDetect = new Mat();
        matForBorder = new Mat();

        float mScale = (float) height / (float) 540;
        mCacheBitmap =
                Bitmap.createBitmap((int) ((float) width / mScale), (int) ((float) height / mScale),
                        Bitmap.Config.ARGB_4444);
        Utils.bitmapToMat(mCacheBitmap, mCache90Mat);
        Core.rotate(mCache90Mat, mCache90Mat, Core.ROTATE_90_CLOCKWISE);

        Log.e("图片尺寸", "mScale = "
                + mScale
                + "，width = "
                + mCacheBitmap.getWidth()
                + "，height = "
                + mCacheBitmap.getHeight());

        mNarrowSize = new Size(mCacheBitmap.getWidth(), mCacheBitmap.getHeight()); // 设置新图片的大小

        //initDetectionCascade();
        compareStrategy.init(getContext());
        Log.e("启动时间", "显示准备完成");
    }

    @Override
    public void onCameraViewStopped() {
        mGray.release();
        mRgba.release();
    }

    @Override
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        Mat tmpRgba = inputFrame.rgba();

        mGray = inputFrame.gray();

        Mat narrowMat = new Mat(mNarrowSize, CvType.CV_8UC1);
        //Gray调整竖屏大小
        Imgproc.resize(mGray, narrowMat, mNarrowSize);
        //Rgba调整竖屏大小
        Imgproc.resize(tmpRgba, tmpRgba, mNarrowSize);

        mCache90Mat.copyTo(matForBorder);
        //使前置的图像也是正的，翻转图像
        if (getCameraIndex() == CameraBridgeViewBase.CAMERA_ID_FRONT) {
            Core.flip(narrowMat, narrowMat, 1);
            Core.flip(tmpRgba, tmpRgba, 1);
        }
        //最小识别面部大小
        if (mAbsoluteFaceSize == 0) {
            int height = narrowMat.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
            if (mNativeDetector != null) {
                mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);
            }
        }

        MatOfRect faces = new MatOfRect();
        if (getDisplayOrientation() == 270) {
            Core.rotate(narrowMat, matForDetect, Core.ROTATE_90_COUNTERCLOCKWISE);
            Core.rotate(tmpRgba, tmpRgba, Core.ROTATE_90_COUNTERCLOCKWISE);
        } else {
            Core.rotate(narrowMat, matForDetect, Core.ROTATE_90_CLOCKWISE);
            Core.rotate(tmpRgba, tmpRgba, Core.ROTATE_90_CLOCKWISE);
        }
        if (mNativeDetector != null) {
            mNativeDetector.detect(matForDetect, faces);
        }
        Rect[] faceArray = faces.toArray();
        for (Rect rect : faceArray) {
            //绘制方框
            Imgproc.rectangle(matForBorder, rect.tl(), rect.br(), FACE_RECT_COLOR, 1);
        }
        for (Rect rect : faceArray) {
            onFace(tmpRgba, rect);
        }

        Rect rect1 = new Rect(90,200,400,400);
        onFace(tmpRgba, rect1);

        faceArray[0] = rect1;

        Core.rotate(matForBorder, mRgba, Core.ROTATE_90_COUNTERCLOCKWISE);
        deliverAndDrawFrame(faceArray.length, mRgba);

        return mRgba;
    }

    /**
     * 绘制方框回调逻辑
     *
     * @param count 绘制个数
     * @param modified 绘图图形背板
     */
    protected void deliverAndDrawFrame(int count, Mat modified) {

        boolean bmpValid = true;
        if (modified != null) {
            try {
                Utils.matToBitmap(modified, mCacheBitmap, true);
            } catch (Exception e) {
                bmpValid = false;
            }
        }
        if (count > 0 && bmpValid && mCacheBitmap != null) {
            if (iFaceShowListener != null) {
                iFaceShowListener.onShow(mCacheBitmap);
            }
        } else {
            if (iFaceShowListener != null) {
                iFaceShowListener.onShow(null);
            }
        }
    }

    public void setMinFaceSize(float faceSize) {
        mRelativeFaceSize = faceSize;
        mAbsoluteFaceSize = 0;
    }

    public void initDetectionCascade() {
        File cascadeFile = RawUtil.getRawFile(getContext(), R.raw.lbpcascade_frontalface);
        if (cascadeFile == null || !cascadeFile.exists()) return;
        mNativeDetector =
                new DetectionBasedTracker(cascadeFile.getAbsolutePath(), mAbsoluteFaceSize);
        boolean isDelete = cascadeFile.delete();
    }

    public void onFace(Mat mat, Rect rect) {
        capture(mat, rect);
        compare(mat, rect);
    }

    private void capture(Mat mat, Rect rect) {
        if (captureFaceCurrent > 0) {
            synchronized (MyNDFaceCameraView.this) {
                if (captureFaceCurrent > 0) {
                    if (captureFaceCurrent == captureFaceMax) {
                        FaceFileUtil.deleteTrain(getContext());
                    }

                    //final boolean isFace1Finish =
                    //        FaceUtil.saveImage(getContext(), mat, rect, FACE1);
                    //if (isFace1Finish) {
                    //    CompareResult cmp = compareStrategy.compare(getContext(), FACE1);
                    //    if (cmp != null && isCompareSuccess(cmp)) {
                    //        return;
                    //    }
                    //}

                    // 保存人脸信息
                    String time = System.currentTimeMillis() + "";
                    final boolean isFinish =
                            FaceUtil.saveTrainImage(getContext(), mat, rect, captureId + time);
                    train(true);

                    if (isFinish) {
                        FaceUtil.saveImage(getContext(), mat, rect, FACE2);
                        final CompareResult cmp = compareStrategy.compare(getContext(), FACE2);
                        if (cmp != null && isCompareSuccess(cmp)) {
                            captureFaceCurrent -= 1;
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    if (iCompareListener != null) {
                                        iCompareListener.onCapture(true);
                                    }
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    private void compare(Mat mat, Rect rect) {
        if (needCompare || isShowResult) {
            synchronized (MyNDFaceCameraView.this) {
                if (needCompare || isShowResult) {
                    if (iCompareListener == null) return;
                    FaceUtil.saveImage(getContext(), mat, rect, FACE2);

                    train(false);
                    final CompareResult cmp = compareStrategy.compare(getContext(), FACE2);

                    if (cmp != null) {
                        needCompare = !isCompareSuccess(cmp);
                        Log.e("识别结果", needCompare ? "识别错误" : "识别成功：" + cmp.getNum());
                    } else {
                        needCompare = true;
                        Log.e("识别结果", "识别错误");
                    }

                    if (!needCompare) {
                        tmpConfirmNumber += 1;

                        Log.e("识别次数", tmpConfirmNumber + "次");
                    } else {
                        tmpConfirmNumber = 0;
                    }

                    // 展示识别结果逻辑
                    if (isShowResult) {
                        if (cmp != null) {
                            if (needCompare) {
                                Imgproc.putText(matForBorder, "false", rect.tl(), 1, 2,
                                        FACE_RECT_COLOR);
                            } else {
                                Imgproc.putText(matForBorder,
                                        String.format(Locale.CHINA, "%.2f----%.2f", cmp.getNum(),
                                                similarity), rect.tl(), 1, 2, FACE_RECT_COLOR);
                            }
                        }
                    }

                    if (tmpConfirmNumber >= lengthPassNumber && !needCompare) {
                        tmpConfirmNumber = 0;
                        post(new Runnable() {
                            @Override
                            public void run() {
                                if (iCompareListener != null) {
                                    iCompareListener.onCompare(cmp);
                                }
                            }
                        });
                    } else {
                        needCompare = true;
                    }
                }
            }
        }
    }

    private boolean compareImage() {
        String file = getComparePath();
        if (TextUtils.isEmpty(file)) return false;
        CompareResult tmp = FaceUtil.compareForFiles(getContext(), FACE2);
        if (tmp == null) return false;
        similarity = tmp.getNum();
        Log.e("图片相似度", similarity + "");
        return true;
    }

    /**
     * 识别是否成功
     *
     * @param cmp 识别结果
     * @return true成功 false失败
     */
    private boolean isCompareSuccess(CompareResult cmp) {
        return !(cmp.getLabel() == 0d || cmp.getLabel() == -1d);
    }

    /**
     * 识别结果回调
     */
    public void setICompareListener(ICompareListener iCompareListener) {
        this.iCompareListener = iCompareListener;
    }

    /**
     * 图像显示回调
     */
    public void setIFaceShowListener(IFaceShowListener iFaceShowListener) {
        this.iFaceShowListener = iFaceShowListener;
    }

    /**
     * 图像训练：已经训练过，不强制训练可以不训练
     *
     * @param force 是否强制训练
     */
    private void train(boolean force) {
        if (force || !hasTrain) {
            compareStrategy.prepare(getContext());

            hasTrain = true;
        }
    }

    /**
     * 获取当前图片地址
     */
    public String getCurrentPath() {
        return FaceFileUtil.getFilePath(getContext(), FACE2);
    }

    /**
     * 获取当前图片地址
     */
    public String getComparePath() {
        String str = FaceFileUtil.getFileTrainParentPath(getContext());
        File file = new File(str);
        if (file.exists()) {
            File[] files = file.listFiles();
            if (files.length != 0) {
                return files[0].getPath();
            }
        }
        return "";
    }

    /**
     * 获取比较策略
     */
    public ICompareStrategy getCompareStrategy() {
        return compareStrategy;
    }

    /**
     * 更换比对策略
     */
    public void setCompareStrategy(ICompareStrategy compareStrategy) {
        this.compareStrategy = compareStrategy;
    }

    /**
     * 设置阈值
     */
    public void setThreshold(double threshold) {
        compareStrategy.setThreshold(threshold);
    }

    /**
     * 识别人脸
     */
    public void compareFace() {
        needCompare = true;
    }

    /**
     * 识别人脸
     *
     * @param compare 是否比较
     */
    public void setCompareFace(boolean compare) {
        needCompare = compare;
    }

    /**
     * 设置截取人脸
     */
    public void setCaptureFace() {
        setCaptureFace(1, captureFaceMax);
    }

    /**
     * 设置截取人脸
     */
    public void addCaptureFace(int id) {
        captureId = id;
        captureFaceMax = 3;
        captureFaceCurrent = 1;
    }

    /**
     * 设置截取人脸图片id和图片数
     *
     * @param id 人脸唯一id
     * @param number 人脸数量
     */
    public void setCaptureFace(int id, int number) {
        captureId = id;
        captureFaceMax = captureFaceCurrent = number;
    }

    /**
     * 切换摄像头
     */
    public void switchCamera() {
        disableView();
        setCameraIndex(mCameraIndex == CAMERA_ID_BACK ? CAMERA_ID_FRONT : CAMERA_ID_BACK);
        setMinFaceSize(mRelativeFaceSize);
        enableView();
    }

    /**
     * 是否图像显示识别结果
     *
     * @param showResult 显示true
     */
    public void setIsShowResult(boolean showResult) {
        isShowResult = showResult;
    }

    /**
     * 连续几次成功才成功
     *
     * @param lengthPassNumber 次数
     */
    public void setLengthPassNumber(int lengthPassNumber) {
        this.lengthPassNumber = lengthPassNumber;
    }
}
